package ezXpns.analysis;

import java.util.UUID;

import ezXpns.storage.StorageListener;

/**
 * An AggregationTask combines all data in a live query into one single piece of data (e.g: sum, average, top 5...)<br />
 * It does so through the help of a Transform and an Aggregator. <br />
 * An AggregationTask is meant to be fed into a live query as a query handler.
 * 
 * @author A0088827L
 *
 * @param <SourceT> Source type or the data type of the storage
 * @param <IntermediateT> Intermediate type, or data type for the aggregator
 * @param <DestT> Final type
 * 
 * @see Transform
 * @see Aggregator
 */

//@author A0088827L
public class AggregationTask<SourceT, IntermediateT, DestT> implements StorageListener<SourceT> {
	private AggregationTask(
		Transform<SourceT, IntermediateT> transform,
		Aggregator<IntermediateT, DestT> aggregator,
		AggregationResultHandler<DestT> handler
	) {
		this.transform = transform;
		this.aggregator = aggregator;
		this.handler = handler;
	}
	
	/**
	 * Create a new aggregation task
	 * @param transform a transform to convert data from the source type to the intermediate type
	 * @param aggregator an aggregator to combine all intermediate data
	 * @param handler a handler to consume the aggregation result
	 * @return an AggregationTask
	 */
	public static <SourceT, IntermediateT, DestT> AggregationTask<SourceT, IntermediateT, DestT> create(
		Transform<SourceT, IntermediateT> transform,
		Aggregator<IntermediateT, DestT> aggregator,
		AggregationResultHandler<DestT> handler
	) {
		return new AggregationTask<SourceT, IntermediateT, DestT>(transform, aggregator, handler);
	}

	@Override
	public void onAdd(UUID id, SourceT item) {
		add(id, item);
		pushResult();
	}

	@Override
	public void onRemove(UUID id, SourceT item) {
		remove(id, item);
		pushResult();
	}

	@Override
	public void onReplace(UUID id, SourceT oldItem, SourceT newItem) {
		remove(id, oldItem);
		add(id, newItem);
		pushResult();
	}
	
	private void add(UUID key, SourceT value) {
		aggregator.addEntry(key, transform.apply(value));
	}
	
	private void remove(UUID key, SourceT value) {
		aggregator.removeEntry(key, transform.apply(value));
	}
	
	private void pushResult() {
		handler.onNewValue(aggregator.getResult());
	}
	
	private Transform<SourceT, IntermediateT> transform;
	private Aggregator<IntermediateT, DestT> aggregator;
	private AggregationResultHandler<DestT> handler;
}
